/*
 * Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 * This file is part of e3log.
 *
 * e3log is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * e3log is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with e3log. If not, see <https://www.gnu.org/licenses/>.
 */

package e3log

import (
	jsoniter "github.com/json-iterator/go"
	"runtime"
	"strings"
)

type stackList struct {
	list []stack
}

func (ts *stackList) E3LogMarshall() string {
	s, _ := jsoniter.MarshalToString(ts.list)
	return s
}

func (ts *stackList) Caller() *stack {
	return ts.list[0].Clone()
}

type stack struct {
	Func string
	File string
	Line int
}

func (ts *stack) E3LogMarshall() string {
	s, _ := jsoniter.MarshalToString(ts)
	return s
}

func (ts *stack) Clone() *stack {
	n := stack{
		Func: ts.Func,
		File: ts.File,
		Line: ts.Line,
	}
	return &n
}

func takeStack(skip int) *stackList {
	const _stackLen = 8
	out := &stackList{
		list: make([]stack, 0, _stackLen),
	}
	toSkip := skip + 2
	pcs := make([]uintptr, _stackLen*2)
	for len(out.list) < _stackLen {
		n := runtime.Callers(toSkip, pcs)
		toSkip += n
		frames := runtime.CallersFrames(pcs[:n])
		for {
			frame, more := frames.Next()
			write := !strings.HasPrefix(frame.Function, "runtime.")
			write = write && !strings.HasPrefix(frame.Function, "gitee.com/ameise84/e3pool/go_pool.")
			write = write && !strings.HasPrefix(frame.Function, "gitee.com/ameise84/e3utils/safe.")

			if write {
				out.list = append(out.list, stack{Func: frame.Function, File: frame.File, Line: frame.Line})
			}
			if !more {
				break
			}
		}
		if n < cap(pcs) {
			break
		}
	}
	return out
}
